#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


void Usage(const std::string &process)
{
    std::cout << "Usage: \n\t" << process << "serverip serverport" << std::endl;
}

enum class Status
{
    NEW,            // 新建状态，单纯连接
    CONNECTING,     // 正在连接，仅仅方便查询conn状态
    CONNECTED,      // 连接/重连 成功
    DISCONNECTED,   // 重连失败
    CLOSED          // 连接失败
};

static const int defalut_sockfd = -1;
static const int defalut_retry_interval = 1;
static const int defalut_max_retries = 5;

class ClientConnection
{
public:
    ClientConnection(std::string &serverip, uint16_t serverport)
        : _sockfd(defalut_max_retries)
        , _serverip(serverip)
        , _serverport(serverport)
        , _retry_interval(defalut_retry_interval)
        , _status(Status::NEW)
        , _max_retries(defalut_max_retries)
    {
    }
    void Connect()
    {
        // 1.创建socket
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd < 0)
        {
            std::cerr << "socket error" << std::endl;
            exit(1);
        }

        // 2.connect
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        inet_pton(AF_INET, _serverip.c_str(), &server.sin_addr);

        int n = connect(_sockfd, (struct sockaddr*)&server, sizeof(server));
        if(n < 0)
        {
            Disconnect();                   // 恢复 _sockfd 默认值，没有连接成功，不代表sockfd没有创建成功
            _status = Status::DISCONNECTED; // 没有连接成功
            return ;
        }
        _status = Status::CONNECTED;    // 连接成功
    }
    int SockFd()
    {
        return _sockfd;
    } 
    void Reconnect()
    {
        _status = Status::CONNECTING;
        int count = 0;
        while(count < _max_retries)
        {
            Connect();  // 重连
            if(_status == Status::CONNECTED) return ;
            sleep(_retry_interval);
            count++;
            std::cout << "重连次数: " << count << ", 最大上限: " << _max_retries << std::endl;
        }
        _status = Status::CLOSED;   //重连失败，关闭
    }
    void Disconnect()
    {
        if(_sockfd != -1)
        {
            close(_sockfd);
            _status = Status::CLOSED;
            _sockfd = -1;
        }
    }
    Status GetStatus()
    {
        return _status;
    }
    void Process()
    {
        // 简单IO
        while(true)
        {
            std::string inbuffer;
            std::cout << "Please Enter: ";
            std::getline(std::cin, inbuffer);
            if(inbuffer.empty()) continue;

            ssize_t n = write(_sockfd, inbuffer.c_str(), inbuffer.size());
            if(n > 0)
            {
                char buffer[1024];
                ssize_t m = read(_sockfd, buffer, sizeof(buffer) - 1);
                if(m > 0)
                {
                    buffer[m] = 0;
                    std::cout << "echo message: " << buffer << std::endl;
                }
                else if(m == 0)
                {
                    _status = Status::DISCONNECTED;
                    break;
                }
                else
                {
                    std::cout << "read m: " << m << "errno" << errno << "errno string: " << strerror(errno) << std::endl;
                    _status = Status::CLOSED;
                    break;
                }
            }
            else
            {
                std::cout << "read m: " << n << "errno" << errno << "errno string: " << strerror(errno) << std::endl;
                _status = Status::CLOSED;
                break;
            }
        }
    }
    ~ClientConnection()
    {
        Disconnect();
    }

private:
    int _sockfd;            
    std::string _serverip;  // server ip ip地址
    uint16_t _serverport;   // server port 端口号
    int _retry_interval;    // 重试时间间隔
    int _max_retries;       // 重试次数
    Status _status;         // 连接状态
};

class TcpClient
{
public:
    TcpClient(std::string serverip, uint16_t serverport)
        : _conn(serverip, serverport)
    {
    }
    void Excute()
    {
        while(true)
        {
            switch(_conn.GetStatus())
            {
                case Status::NEW:
                    _conn.Connect();
                    break;
                case Status::CONNECTED:
                    std::cout << "连接成功，开始通信" << std::endl;
                    _conn.Process();
                    break;
                case Status::DISCONNECTED:
                    std::cout << "连接失败或对方掉线，开始重连" << std::endl;
                    _conn.Reconnect();
                    break;
                case Status::CLOSED:
                    _conn.Disconnect();
                    std::cout << "重连失败，退出" << std::endl;
                    return ;    // 退出
                default:
                    break;
            }
        }
    }
    ~TcpClient()
    {

    }

private:
    ClientConnection _conn;
};

// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }

    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);
    TcpClient client(serverip, serverport);
    client.Excute();

    return 0;
}